﻿/* 
 * Developer: Шиванов Даниил
 */



using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using System.IO.IsolatedStorage;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Linq;
using System.Windows.Data;
using System.Resources;


namespace Display
{
    

    

    public partial class DisplayForm : UserControl
    {
        #region Важно: Переменные для дебага

        /// <summary>
        /// Если true создает стандартные файлы. Как будто уже закаченные.
        /// </summary>
        private bool DebugLoadTestSettings = false;


        /// <summary>
        /// Проигрывать ли видео. Для работы положить видеоролик в изолированое хранилище и указать его адрес в PlayCurrentVideo.
        /// </summary>
        private bool DebugVideo = false;


        /// <summary>
        /// Есть ли связь с сервером.
        /// </summary> 
        private bool DebugServer = true;

        #endregion

        #region Переменные

        /// <summary>
        /// Таймер запускающий видео.
        /// </summary>
        DispatcherTimer VideoTimer = new DispatcherTimer();

        /// <summary>
        /// Интервал через который проигрывается видео.
        /// </summary>
        int VideoTimerInterval = 60;

        bool PossibilityOfUpdateVideo = true;

        /// <summary>
        /// Таймер отправки скриншота
        /// </summary>
        DispatcherTimer ScreenTimer = new DispatcherTimer();

        int ScreenTimerInterval = 60; 

        /// <summary>
        /// Таймер проверки обновлений
        /// </summary>
        DispatcherTimer UpdateTimer = new DispatcherTimer();

        int UpdateTimerInterval = 60; 

        /// <summary>
        /// Ежесекундное обновление.
        /// </summary>
        DispatcherTimer EverySecondUpdate = new DispatcherTimer();

        /// <summary>
        /// Показывает текущее время
        /// </summary>
        public static DateTime CurrentTime = new DateTime();

        /// <summary>
        /// Здесь хранится текущее рассписание.
        /// </summary>
        List<ScheduleService.wActualScheduleNode> Schedule = new List<ScheduleService.wActualScheduleNode>();
        
        /// <summary>
        /// Три ближайших рейса для вывода на дисплей
        /// </summary>
        IEnumerable<ScheduleService.wActualScheduleNode> NearestSchedule = new List<ScheduleService.wActualScheduleNode>();

        /// <summary>
        /// Список рейсов на которые есть посадки в ближайший час
        /// </summary>
        IEnumerable<ScheduleService.wActualScheduleNode> NearestBoarding = new List<ScheduleService.wActualScheduleNode>();

        /// <summary>
        /// Текущие посадки.
        /// </summary>
        IEnumerable<ScheduleService.wActualScheduleNode> CurrentBoarding = new List<ScheduleService.wActualScheduleNode>();


        /// <summary>
        /// Имя текущего видео
        /// </summary>
        private string CurrentVideo;

        /// <summary>
        /// Показывается ли сейчас какое-нибудь видео
        /// </summary>
        private bool VideoPlay=false;

        /// <summary>
        /// Таймер смены языков
        /// </summary>
        DispatcherTimer LangTimer = new DispatcherTimer();

        /// <summary>
        /// Класс пассажиров на разных языках
        /// </summary>
        private string DisplayPassengerClass;

        /// <summary>
        /// Бегущая строка
        /// </summary>
        private string TickerText;
        
        /// <summary>
        /// Язык бегущей строки меняется отдельно от общего языка, поэтому отдельная переменная указывающая на текущий язык.
        /// </summary>
        private string TickerTextLang="";

        /// <summary>
        /// Состояние дисплея
        /// </summary>
        private StateEnum State = StateEnum.boarding;

        /// <summary>
        /// Разница во времени между сервером и клиентом
        /// </summary>
        TimeSpan ServerTimeSpan;

       
        // Версии данных
        int SchedulesDataVersion = -1;
        int SettingsDataVersion = -1;

        /// <summary>
        /// Идентификатор клиента
        /// </summary>
        int ClientId = 1;
       
        #endregion

        #region Енумы
        public enum LoadFileType
        {
            All,
            Schedule,
            Config
        }

        public enum StateEnum
        {
            wait,
            boarding,
            video
        }
        #endregion

        // ==============================================================================
        // ==============================================================================

        #region Открытие

        public DisplayForm()
        {
            InitializeComponent();

            Open(); 
        }

        
        /// <summary>
        /// Запускается при открытии программы
        /// </summary>
        private void Open()
        {

            
            try
            {
                using (IsolatedStorageFile tIso = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    //Создаем нужные папки в изолированном хранилище
                    tIso.CreateDirectory(@"Display");
                    tIso.CreateDirectory(@"Display\Video");
                }
            }
            catch { }

            
            ServerTimeSpan = DateTime.Now - new DateTime(2011, 11, 14, 15, 15, 0);
            //Ставим таймеры
            UpdateTimers();

            //Настройка видео анимации
            VideoElement.IsMuted = true;
            VideoElement.MediaEnded += new RoutedEventHandler(VideoEnd);

            AnimationCanvas_anim_endvideo.Completed += new EventHandler(ShowTable);
            AnimationCanvas_anim_endtable.Completed += new EventHandler(ShowVideo);

            //Загружаем скаченные файлы из хранилища. Если не получилось, показываем черный экран.
            LoadSettings(LoadFileType.All);
           
            //После загрузки языков устанавливаем настройки бегущей строки.
            //TickerTextLang = LocText.CurrentLanguage;
            RefreshTicker(this,new EventArgs());


            //Тут должен сначала проверяетя фаил с версией. Если его нет то запрашивается ClientId и только 
            //после этого будет проверка обновления.

            //Обновление
            CheckUpdate(); 
        }

        private void UpdateTimers(){
            EverySecondUpdate.Interval = new TimeSpan(0, 0, 1);
            EverySecondUpdate.Tick += new EventHandler(EverySecond);
            EverySecondUpdate.Start();

            VideoTimer.Interval = new TimeSpan(0, 0, VideoTimerInterval);
            VideoTimer.Tick += new EventHandler(PlayCurrentVideo);
            VideoTimer.Start();

            ScreenTimer.Interval = new TimeSpan(0, 0, ScreenTimerInterval);
            VideoTimer.Start();

            UpdateTimer.Interval = new TimeSpan(0, 0, UpdateTimerInterval);
            VideoTimer.Tick += new EventHandler(CheckUpdate);
            VideoTimer.Start();

            //LangTimer.Interval = new TimeSpan(0, 0, 10);
            //LangTimer.Tick += new EventHandler(ChangeLanguage);
            //LangTimer.Start();

        }


        /// <summary>
        /// Загружает информацию из скачанных настроек
        /// </summary>
        public void LoadSettings(LoadFileType filetype, bool backup=false)
        {
            bool tResult = true; //Результат загрузки.

            #region Простыни Загрузки
            if (filetype == LoadFileType.Schedule || filetype == LoadFileType.All) 
            {
                List<ScheduleService.wActualScheduleNode> tList;
                try
                {
                    using (IsolatedStorageFile tIso = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        IsolatedStorageFileStream tFileStream;

                        string tPath = @"Display\Schedule";
                        if (backup) tPath += "_backup";

                        tFileStream = new IsolatedStorageFileStream(tPath+".xml", FileMode.Open, tIso);

                        XmlSerializer tXmlSerializer =
                            new XmlSerializer(typeof(List<ScheduleService.wActualScheduleNode>));
                        tList = (List<ScheduleService.wActualScheduleNode>)tXmlSerializer.Deserialize(tFileStream);

                        tFileStream.Close();
                        Schedule = tList;
                        tResult &= true;
                    }
                }
                catch
                {
                    
                }
             
            }



            if (filetype == LoadFileType.Config || filetype == LoadFileType.All)
            {
                SettingsService.ClientNotEF tClientData;
                try
                {
                    using (IsolatedStorageFile tIso = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        IsolatedStorageFileStream tFileStream;
                        string tPath = @"Display\Settings";
                        if (backup) tPath += "_backup";

                        tFileStream = new IsolatedStorageFileStream(tPath + ".xml", FileMode.Open, tIso);

                        XmlSerializer tXmlSerializer =
                            new XmlSerializer(typeof(SettingsService.ClientNotEF));
                        tClientData = (SettingsService.ClientNotEF)tXmlSerializer.Deserialize(tFileStream);

                        tFileStream.Close();


                        DisplayPassengerClass = tClientData.ClassOfPassengers.Name;
                        UpdateTimerInterval = tClientData.PingFrequency;
                        PossibilityOfUpdateVideo = tClientData.PossibilityOfUpdateVideo;
                        ScreenTimerInterval =  tClientData.ScreenshotFrequency;
                        TickerText = tClientData.Ticker;
                        VideoTimerInterval = tClientData.VideoInterruptFrequency;
                        UpdateTimers();
                        tResult &= true;
                    }
                }
                catch
                {

                }

            }
             
            #endregion

            //Делаем выборку
            var tNearestSchedule = from obj in Schedule
                                   where obj.TimeOfDeparture >= CurrentTime
                                   orderby obj.TimeOfDeparture
                                   select obj;

            NearestSchedule = tNearestSchedule.Take(3);

            NearestBoarding = from obj in tNearestSchedule
                              where (new TimeSpan(1, 0, 0)) >= (obj.TimeOfLanding - CurrentTime)
                              select obj;

            CurrentBoarding = from obj in tNearestSchedule
                              where CurrentTime >= obj.TimeOfLanding
                              select obj;
            //Обновляем текст
            UpdateText();

            //Если дисплей не может загрузить какие-либо данные из файлов или бэкапов он показывает пустой экран.
            if (!tResult)
            {
                if (FullCanvasRect.Visibility == Visibility.Collapsed) FullCanvasRect.Visibility = Visibility.Visible;
            }
            else
            {
                if (FullCanvasRect.Visibility == Visibility.Visible) FullCanvasRect.Visibility = Visibility.Collapsed;
            }
        }

        #endregion

        // ==============================================================================
        // ==============================================================================

        #region Обновление

        

        /// <summary>
        /// Проверяет наличие обновлений
        /// </summary>
        private void CheckUpdate()
        {
            
            


            if (DebugServer)
            {
                PingService.PingServiceClient tPingServiceClient = new PingService.PingServiceClient();
                tPingServiceClient.PingExCompleted += new EventHandler<PingService.PingExCompletedEventArgs>(PingExComplite);

                tPingServiceClient.PingExAsync(ClientId);
            }
             
        }

        /// <summary>
        /// Срабатывает по таймеру проверки обновлений
        /// </summary>
        public void CheckUpdate(object sender, EventArgs e)
        {
            CheckUpdate();
        }

        /// <summary>
        /// Запускает обновления если необходимо.
        /// </summary>
        private void PingExComplite(object sender, PingService.PingExCompletedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                PingService.wClientChanx tClientChanx = e.Result;

                if (tClientChanx.SchedulesDataVersion > SchedulesDataVersion)
                {
                    UpdateSchedules();
                }

                if (tClientChanx.SettingsDataVersion > SettingsDataVersion)
                {
                    UpdateSettings();
                }
                
            }
        }


        /// <summary>
        /// Команда на обновление рассписания
        /// </summary>
        private void UpdateSchedules() 
        {
            ScheduleService.ScheduleServiceClient tScheduleClient = new ScheduleService.ScheduleServiceClient();

            ObservableCollection<DateTime> tDateList = new ObservableCollection<DateTime>();

            tDateList.Add(new DateTime(2011, 11, 14));
            tDateList.Add(new DateTime(2011, 11, 15));
            tScheduleClient.GetActualScheduleCompleted += new EventHandler<ScheduleService.GetActualScheduleCompletedEventArgs>(UpdateSchedulesComplite);
            tScheduleClient.GetActualScheduleAsync(ClientId, tDateList);

        }


        /// <summary>
        /// Завершение обновления расписания
        /// </summary>
        private void UpdateSchedulesComplite(object sender, ScheduleService.GetActualScheduleCompletedEventArgs e)
        {
            bool tResult = false;
            if (e.Error == null && !e.Cancelled)
            {
                ScheduleService.ActualScheduleResult tSchedule = e.Result;
                ObservableCollection<ScheduleService.wActualScheduleNode> tList = tSchedule.Data;
                
                try
                {
                    using (IsolatedStorageFile tIso = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        IsolatedStorageFileStream tFileStream;

                       
                        //Прежде всего бэкап
                        if (tIso.FileExists(@"Display\Schedule.xml"))
                            tIso.CopyFile(@"Display\Schedule.xml", @"Display\Schedule_backup.xml",true);
                       
                        //Потом запись
                        tFileStream = new IsolatedStorageFileStream(@"Display\Schedule.xml", FileMode.Create, tIso);
                       
                        XmlSerializer tXmlSerializer = 
                            new XmlSerializer(typeof(ObservableCollection<ScheduleService.wActualScheduleNode>));
                       
                        tXmlSerializer.Serialize(tFileStream,tList);
                        
                        tFileStream.Close();

                        SchedulesDataVersion = tSchedule.DataVersion.DataVersion;

                        tResult = true;
                    }
                }
                catch 
                {
                    
                }
                if (tResult) 
                {
                    LoadSettings(LoadFileType.Schedule);
                }
            }

            //При положительном результате закачки приложение обновит текущее расписание.
            if (tResult) 
            {
                LoadSettings(LoadFileType.Schedule);
            }
        }


        /// <summary>
        /// Команда на обновление настроек
        /// </summary>
        private void UpdateSettings() 
        {
            SettingsService.ClientSettingsServiceClient tSettingsService = new SettingsService.ClientSettingsServiceClient();
            tSettingsService.GetClientSettingsCompleted += new EventHandler<SettingsService.GetClientSettingsCompletedEventArgs>(UpdateSettingsComplite);
            tSettingsService.GetClientSettingsAsync(ClientId);
        }

        private void UpdateSettingsComplite(object sender, SettingsService.GetClientSettingsCompletedEventArgs e)
        {
            bool tResult = false;
            if (e.Error == null && !e.Cancelled)
            {

                SettingsService.ClientSettingsResult tSettings = e.Result;
                SettingsService.ClientNotEF tClientData = tSettings.Data;
                try
                {
                    using (IsolatedStorageFile tIso = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        
                        IsolatedStorageFileStream tFileStream;

                       
                        //Прежде всего бэкап
                        if (tIso.FileExists(@"Display\Settings.xml"))
                            tIso.CopyFile(@"Display\Settings.xml", @"Display\Settings_backup.xml", true);
                       
                        //Потом запись
                        tFileStream = new IsolatedStorageFileStream(@"Display\Settings.xml", FileMode.Create, tIso);
                       
                        XmlSerializer tXmlSerializer = 
                            new XmlSerializer(typeof(SettingsService.ClientNotEF));

                        tXmlSerializer.Serialize(tFileStream, tClientData);
                        
                        tFileStream.Close();
                        SettingsDataVersion = tSettings.DataVersion.DataVersion;
                        tResult = true;
                    }
                }
                catch
                {
                    
                }
                if (tResult)
                {
                    LoadSettings(LoadFileType.Config);
                }
            }
        }

        #endregion

        // ==============================================================================
        // ==============================================================================

        #region Отображение

        // ==============================================================================

        #region Видео
        /// <summary>
        /// Анимация входа в таблицу(после завершения видео) 
        /// </summary>
        public void ShowTable(object sender, EventArgs e)
        {
            VideoCanvas.Visibility = Visibility.Collapsed;
            AnimationCanvas_anim.Begin();
            VideoTimer.Start();
            VideoPlay = false;
        }

        /// <summary>
        /// Запускает текущее видео из плейлиста
        /// </summary>
        public void PlayCurrentVideo() {
            if (DebugVideo)
            if (!VideoPlay)
            {
                VideoTimer.Stop();
                PlayVideo(@"Display\Video\Wildlife.wmv");
            }
        }

        /// <summary>
        /// Срабатывает по таймеру запуска видео
        /// </summary>
        public void PlayCurrentVideo(object sender, EventArgs e)
        {
            PlayCurrentVideo();
        }

        /// <summary>
        /// Анимация перед показом видео.
        /// </summary>
        public void ShowVideo(object sender, EventArgs e)
        {
            VideoElement.Stop();
            VideoElement.Play();
            VideoCanvas.Visibility = Visibility.Visible;
            AnimationCanvas_anim.Begin();
        }

        /// <summary>
        /// Анимация завершения видео
        /// </summary>
        void VideoEnd(object sender, EventArgs e)
        {
            if (State == StateEnum.video)
            {
                VideoPlay = false;
                PlayCurrentVideo();
            }
            else 
            {
                AnimationCanvas_anim_endvideo.Begin();
            }
        }

        /// <summary>
        /// Загружает и проигрывает видео из изолированого хранилища.
        /// </summary>
        public void PlayVideo(string VideoName)
        {
            if (!VideoPlay)
            {

                if (CurrentVideo != VideoName)
                {
                    try
                    {
                        using (IsolatedStorageFile tIso = IsolatedStorageFile.GetUserStoreForApplication())
                        {
                            if (tIso.FileExists(VideoName))
                            {
                                IsolatedStorageFileStream tFileStream = tIso.OpenFile(VideoName, System.IO.FileMode.Open, FileAccess.Read);
                                VideoElement.SetSource(tFileStream);
                            }
                        }
                        
                        CurrentVideo = VideoName;
                    }
                    catch { }
                }
                VideoPlay = true;
                AnimationCanvas_anim_endtable.Begin();
            };
        }

        #endregion

        // ==============================================================================

        #region Остальное

        /// <summary>
        /// Возвращает бегущую строку обратно
        /// </summary>
        private void RefreshTicker(object sender, EventArgs e)
        {
            TickerAnimation.Begin();
        }

        /// <summary>
        /// Апдейтер незабинденных в силу сложности или лени текстов. Срабатывает при смене языков.
        /// </summary>
        public void UpdateText(){
            //В EverySecond есть незабинденные часики поэтому вызываем внеочереди
            EverySecond(this, new EventArgs());
            TableBottom_Class.Text = Localization.PClass + DisplayPassengerClass;
            TickerTextString.Text = TickerText;
        }

        /// <summary>
        /// Обновляет время и выполняет другие полезные функции.
        /// </summary>
        void EverySecond(object sender, EventArgs e)
        {
            //Здесь происходит обновление часов и таблицы. Обновлять их каждый тик слишком жирно. Поэтому обновляется каждую секунду.


            CurrentTime = DateTime.Now - ServerTimeSpan; //К текущему времени прибавляется разница.

            //Часики сверху имеют сложную структуру...

            TableTop_PresentTime.Text = CurrentTime.ToString("HH:mm:ss");
            Run tRun = new Run();
            tRun.Text = "   " + CurrentTime.ToShortDateString() + ", " + 
                Localization.ResourceManager.GetString(CurrentTime.DayOfWeek.ToString(), Localization.Culture);

            tRun.FontSize = 10;
            TableTop_PresentTime.Inlines.Add(tRun);
           
            //Обновление данных таблицы.
            TableData.ItemsSource=null;
            TableData.ItemsSource=NearestSchedule;

            //Обновления режимов
            
            if (CurrentBoarding.Count() > 0) { 
                //Режим посадки
                if (VideoTimer.IsEnabled) 
                    VideoTimer.Stop();
                State = StateEnum.boarding;
            }
            else if (NearestBoarding.Count() > 0)
            {
                
                //Режим ожидания посадки
                if (VideoTimer.Interval.TotalSeconds != VideoTimerInterval)
                    VideoTimer.Interval = new TimeSpan(0, 0, VideoTimerInterval);
                if (!VideoTimer.IsEnabled && !VideoPlay)
                    VideoTimer.Start();
                State = StateEnum.wait;
                
            }
            else { 
                
                //Режим видео
                if (VideoTimer.IsEnabled)
                    VideoTimer.Stop();
                if (State != StateEnum.video) {
                    PlayCurrentVideo();
                }
                State = StateEnum.video;
                
            }
        }

        #endregion

        // ==============================================================================
        
        #endregion

        // ==============================================================================
        // ==============================================================================

        
    }

}
